package cfca.paperless.client.servlet._4_manager._4_02_AutoUpdateSeal;

import java.io.File;
import java.io.FileInputStream;
import java.security.PrivateKey;

import org.apache.commons.io.FileUtils;

import cfca.paperless.client.bean.SealCertBean;
import cfca.paperless.client.servlet.PaperlessConfig;
import cfca.paperless.client.servlet.PaperlessManagerClient;
import cfca.paperless.client.util.Base64;
import cfca.paperless.client.util.IoUtil;
import cfca.paperless.client.util.StringUtil;
import cfca.sadk.algorithm.common.GMObjectIdentifiers;
import cfca.sadk.algorithm.common.Mechanism;
import cfca.sadk.asn1.parser.ASN1Parser;
import cfca.sadk.org.bouncycastle.asn1.ASN1Encodable;
import cfca.sadk.org.bouncycastle.asn1.ASN1Sequence;
import cfca.sadk.org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey;
import cfca.sadk.util.CertUtil;
import cfca.sadk.util.KeyUtil;
import cfca.sadk.x509.certificate.X509Cert;

/**
 * @Description 自动化更新印章接口，使用示例。 
 * 1，外部传入预先生成的印模文件数据，可以为空，为空时原印模不变。
 * 2，印章证书更新标识，0：不更新；2：使用外部预先生成的PFX文件数据对印章证书进行更新。
 * 3，外部传入预先生成的PFX文件数据，需要指定证书的私钥密码。
 * 4，待更新的印章信息，需要指定印章密码。
 * 
 * @Author dell
 * @Date 2016-5-13
 * @CodeReviewer TODO
 */
public class AutoUpdateSealTest02 {

    public static void main(String[] args) throws Exception {
        try {
            PaperlessManagerClient clientBean = new PaperlessManagerClient(PaperlessConfig.managerUrl, 10000, 60000);// 无纸化管理接口的访问URL

            byte[] sealImage = FileUtils.readFileToByteArray(new File("./TestData/11.png"));// 印模数据，可以为空，为空时原印模不变

            String sealCertUpdateFlag = "2";// 印章证书更新标识，0：不更新；2：使用外部预先生成的PFX文件数据对印章证书进行更新。设置为0时，参数sealCertXml可以为空。

            String customerType = "1";// 1:个人；2：企业
            String keyAlg = "RSA";// RSA/SM2
            String keyLength = "2048";// 1024/2048/256
            String userName = "用户名008";// 用户名
            String identificationType = "0";// 证件类型
            String identificationNo = "110120201801231234";// 证件号码

            // PFX文件数据
            byte[] pfxFileData = FileUtils.readFileToByteArray(new File("./TestData/cert/cert0012-cfca4321.pfx"));// 外部传入预先生成的PFX文件数据，需要保证前面的这些信息准确无误
            String pfxFileString = new String(Base64.encode(pfxFileData, "UTF-8"));

            String privateKeyPassword = "cfca4321";// 证书的私钥密码，不指定的话则使用8位随机数字代替，请务必指定密码！！！
            String privateKeyPasswordBase64 = new String(Base64.encode(privateKeyPassword, "UTF-8"));

            SealCertBean sealCertBean = new SealCertBean(customerType, keyAlg, keyLength, userName, identificationType, identificationNo);
            sealCertBean.setPkcs12(pfxFileString);
            sealCertBean.setPkcs12Password(privateKeyPasswordBase64);
            
            boolean checkResult = checkPfxFileData(pfxFileData, privateKeyPassword, keyAlg, keyLength);// 检查pfx文件数据是否有效，以及和策略信息相对应
            if (!checkResult) {
                return;
            }

            String sealCertXml = sealCertBean.toString();
            System.out.println(sealCertXml);

            StringBuilder sb = new StringBuilder();
            sb.append("<SealInfo>");
            sb.append("<SealCode>").append("6674990468").append("</SealCode>");// 已经在服务端生成的、需要做更新操作的印章编码
            sb.append("<SealPassword>").append(Base64.encode("11111111", "utf-8")).append("</SealPassword>");// 印章密码，待更新的印章已经设置过密码时需要验证
            sb.append("<SealName>").append("印章001").append("</SealName>");
            sb.append("<SealPerson>").append("SealPerson001").append("</SealPerson>");
            sb.append("<SealOrg>").append("SealOrg001").append("</SealOrg>");
            sb.append("</SealInfo>");
            String sealInfoXml = sb.toString();
            System.out.println(sealInfoXml);

            // 操作员编码或者机构号
            String operatorCode = PaperlessConfig.operatorCode;

            // 自动化更新印章
            String result = clientBean.autoUpdateSeal(sealImage, sealCertUpdateFlag, sealCertXml, sealInfoXml, operatorCode);

            String code = StringUtil.getNodeText(result, "Code");

            if ("200".equals(code)) {
                System.out.println("OK:autoUpdateSeal");
                System.out.println(result);
            } else {
                // Code不等于200时为异常,处理结果为异常，打印出错误码和错误信息
                System.out.println("NG:autoUpdateSeal");
                System.out.println(result);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static boolean checkPfxFileData(byte[] pfxFileData, String privateKeyPassword, String keyAlg, String keyLength) throws Exception {

        PrivateKey privateKey = null;
        X509Cert x509Cert = null;

        if (isSM2(pfxFileData)) {
            privateKey = KeyUtil.getPrivateKeyFromSM2(pfxFileData, privateKeyPassword);
            x509Cert = CertUtil.getCertFromSM2(pfxFileData);
            if (!Mechanism.SM2.equals(keyAlg)) {
                throw new Exception("invalid p12Data. keyAlg error");
            }
        }
        else {
            privateKey = KeyUtil.getPrivateKeyFromPFX(pfxFileData, privateKeyPassword);
            x509Cert = CertUtil.getCertFromPFX(pfxFileData, privateKeyPassword);
            if (!Mechanism.RSA.equals(keyAlg)) {
                throw new Exception("invalid p12Data. keyAlg error");
            }
        }

        String algorithm = x509Cert.getPublicKey().getAlgorithm();
        
        if (Mechanism.RSA.equals(algorithm)) {
            BCRSAPublicKey rsaPublicKey = (BCRSAPublicKey) x509Cert.getPublicKey();
            int algorithmLength = (rsaPublicKey.getModulus().bitLength() + 7) / 8 * 8;
            
            if (!String.valueOf(algorithmLength).equals(keyLength)) {
                throw new Exception("invalid p12Data. keyLength error");
            }
        }
        else if (Mechanism.SM2.equals(algorithm)) {
            if (!"256".equals(keyLength)) {
                throw new Exception("invalid p12Data. keyLength error");
            }
        }

        return true;
    }

    public static boolean isSM2(byte[] p12Data) throws Exception {

        boolean result = false;

        if (ASN1Parser.isBase64Compatability(p12Data)) {
            p12Data = Base64.decode(p12Data);
        }
        ASN1Sequence seq = ASN1Sequence.getInstance(p12Data);
        if (seq.size() != 3) {
            throw new Exception("invalid p12Data");
        }

        ASN1Sequence content = ASN1Sequence.getInstance(seq.getObjectAt(1));

        ASN1Encodable oid = content.getObjectAt(0);

        if (GMObjectIdentifiers.sm2Data.equals(oid)) {
            result = true;
        }

        return result;
    }

}
